﻿#include "mainwindow.h"
#include "ui_mainwindow.h"

#pragma execution_character_set("utf-8")
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    screen_init();
    display_init();
    //alg_init();
    callback_register();
}

MainWindow::~MainWindow()
{
    delete ui;
}


void MainWindow::screen_init()
{
    //uart components init
    port_name = serial.refresh_port();
    ui->comselect->addItems(port_name);
    QList<QString> baudrate_list;
    baudrate_list << "921600"<<"115200"<<"57600"<<"9600";
    ui->baudselect->addItems(baudrate_list);

    ui->liquid_radio->setChecked(true);

    ui->liquid_radio->setDisabled(true);
    ui->level_radio->setDisabled(true);

    QString folderPath = QApplication::applicationDirPath() + QDir::separator() + "log";
    current_time = QDateTime::currentDateTime().toString("MM-dd@hh_mm_ss");
    qDebug() << folderPath;
    QString folderPath1 = folderPath + QDir::separator() + current_time;
    qDebug() << folderPath1;
    QDir dir(folderPath);
    QDir dir1(folderPath1);
	if (dir.mkpath(".")) {
		qDebug() << "Folder created successfully!";
	}
	else {
		qDebug() << "Folder existed";
	}
	dir1.mkpath(".");//创建log文件夹，用MM-dd@hh_mm_ss来区分时间格式。
    QDir dir2(folderPath1 + QDir::separator() + "speed");
    QDir dir3(folderPath1 + QDir::separator() + "range");
    dir2.mkpath(".");
    dir3.mkpath(".");
}


void MainWindow::display_init()
{
    charttimer = std::make_unique<QTimer>();
    //chartView = std::make_unique<QChartView>();
    series = std::make_unique<QLineSeries>();
    series1 = std::make_unique<QLineSeries>();
    text = std::make_unique<QGraphicsTextItem>();
    text1 = std::make_unique<QGraphicsTextItem>();
    fft_mag_data = std::make_unique<std::vector<uint16_t>>(4096);
    axisX = std::make_unique<QValueAxis>();
    axisY = std::make_unique<QValueAxis>();

    chart = std::make_unique<QChart>();
    chart->addSeries(series.get());
    chart->addSeries(series1.get());
    chart->createDefaultAxes();
    QValueAxis* m_axisX = (QValueAxis*)chart->axes(Qt::Horizontal, series.get()).at(0);//获取图表中的横坐标轴
    QValueAxis* m_axisY = (QValueAxis*)chart->axes(Qt::Vertical, series.get()).at(0);  //获取图表中的纵坐标轴
    m_axisX->setGridLineVisible(true);                                           //设置网格线可见
    m_axisX->setRange(-21, 21);                                              //设置坐标轴范围
    m_axisX->setTickCount(10);       
    m_axisX->setTitleText("距离"); 
    m_axisY->setRange(0, 100);
    m_axisY->setGridLineVisible(true);
    m_axisY->setTickCount(10);
    m_axisY->setTitleText("能量");


    chartView = new ChartView(chart.get());
    chartView->setRenderHint(QPainter::Antialiasing); // 可选的渲染选项
    //chartView->setRubberBand(QChartView::HorizontalRubberBand);
    chartView->setViewport(new QOpenGLWidget); // 使用 OpenGL 加速
    chartView->setChart(chart.get());
    ui->gridLayout_2->addWidget(chartView);

    chart->scene()->removeItem(text.get());
    chart->scene()->removeItem(text1.get());
}


void MainWindow::alg_init()
{
    //tracker1D_init(res1);
}


void MainWindow::callback_register()
{
    connect(ui->refreshbutton,SIGNAL(clicked()),this,SLOT(refresh_driver()));
    connect(ui->combutton,SIGNAL(clicked()),this,SLOT(driver_control()));
    connect(&serial,SIGNAL(get_pingpu_data(QByteArray*, uint16_t*)),this,SLOT(data_display(QByteArray*,uint16_t*)));
    connect(&serial, SIGNAL(get_range_data(QByteArray*)), this, SLOT(range_display(QByteArray*)));

    data_generate_thread = std::thread(&MainWindow::data_generate_task, this);
    data_generate_thread.detach();
    data_store_thread = std::thread(&MainWindow::data_store_task, this);
    data_store_thread.detach();

    std::thread t(&MainWindow::obj_store_task, this);
    t.detach();

    connect(charttimer.get(),SIGNAL(timeout()),this,SLOT(update_chart()));

    charttimer->start(10);
}


void MainWindow::refresh_driver()
{
    if (!serial.status())
    {
        ui->comselect->clear();
        port_name = serial.refresh_port();
        ui->comselect->addItems(port_name);
    }
}


void MainWindow::driver_control()
{
    QString err;
    int ret;
    if(!serial.status())
    {
        if(ui->comselect->currentText().isEmpty())
        {
            err = "没有找到串口";
            goto error;
        }

        ret = serial.com_open(ui->comselect->currentText(), ui->baudselect->currentText().toInt());
        if(ret == -1)
        {
            err = "串口故障或串口占用";
            goto error;
        }
        ui->combutton->setText("关闭串口");
        ui->comselect->setDisabled(true);
        ui->baudselect->setDisabled(true);
    }
    else
    {
        ui->combutton->setText("打开串口");
        serial.com_close();
        ui->comselect->setDisabled(false);
        ui->baudselect->setDisabled(false);
    }

    return ;

error:
    QMessageBox::warning(this,QString("ERROR"),err);
}

void MainWindow::update_chart()
{
    std::unique_lock<std::mutex> lock(mtx);
    if(!point_data.empty())
    {
        //chart->removeSeries(series.get());
        //series->clear();
        //chart->scene()->removeItem(text.get());
        QList<QPointF> temp = point_data.front();
        point_data.pop();
        QList<QPointF> temp1 = threshold_data.front();
        threshold_data.pop();


        series->replace(temp);
        series1->replace(temp1);

        QFont font("Arial", 20, QFont::Bold);
        text->setFont(font);

        if (mode == MODE_LEVEL)
        {
            text->setPlainText(QString::asprintf("range:%.5fm,", range) + QString("value:%2LSB").arg(maxIndex));
        }
        else if (mode == MODE_LIQUID)
        {
            text->setPlainText(QString("speed:%1m/s,value:%2LSB").arg(speed).arg(maxIndex));
        }

        text->setPos(800, 200);

        if (mode == MODE_LEVEL)
        {
            QFont font("Arial", 16);
            text1->setFont(font);
            QString str;
#if 1
            for (uint32_t i = 0; i < tracker1D_output->m_cnt; i++)
            {
                str +=  QString("r%1:").arg(tracker1D_output->m_data[i].m_id) + QString::asprintf("%.5fm", tracker1D_output->m_data[i].m_distance);
            }
#else 
            for (uint32_t i = 0; i < range_temp_num; i++)
            {
                str += QString("r%1:").arg(i + 1) + QString::asprintf("%.5fm,", range_temp[i]);
            }
#endif           
            text1->setPlainText(str);
            text1->setPos(200, 100);
        }

       
        chart->scene()->removeItem(text.get());
        chart->scene()->addItem(text.get());
        chart->scene()->removeItem(text1.get());
        chart->scene()->addItem(text1.get());


    }

}





void MainWindow::mode_handle()
{
    if (ui->level_radio->isChecked())
    {
        mode = MODE_LEVEL;
        QValueAxis* m_axisX = (QValueAxis*)chart->axes(Qt::Horizontal, series.get()).at(0);
        m_axisX->setRange(0, 160);
        QValueAxis* m_axisY = (QValueAxis*)chart->axes(Qt::Vertical, series.get()).at(0);  //获取图表中的纵坐标轴
        m_axisY->setRange(0, 100);
        chartView->range_update();
        for (int i = 0; i < fft_mag_data->size(); ++i)
        {
            data[i] = i * res1;
        }
    }
    else if (ui->liquid_radio->isChecked())
    {
        mode = MODE_LIQUID;
        QValueAxis* m_axisX = (QValueAxis*)chart->axes(Qt::Horizontal, series.get()).at(0);
        m_axisX->setRange(-21, 21);
        QValueAxis* m_axisY = (QValueAxis*)chart->axes(Qt::Vertical, series.get()).at(0);  //获取图表中的纵坐标轴
        m_axisY->setRange(0, 100);
        chartView->range_update();
        for (int i = 0; i < fft_mag_data->size() / 2; ++i)
        {
            data[i] = (i + 1) * res;
        }
        for (int i = 0; i < fft_mag_data->size() / 2; ++i)
        {
            data[i + fft_mag_data->size() / 2] = -(static_cast<float>(fft_mag_data->size()) / 2 - (i + 1)) * res;
        }
    }
}

void MainWindow::data_generate_task()
{
    if (mode == MODE_LEVEL)
    {
        for (int i = 0; i < fft_mag_data->size(); ++i)
        {
            data[i] = i * res1;
        }
    }
    else
    {
        for (int i = 0; i < fft_mag_data->size() /2; ++i)
        {
            data[i] = (i+1)*res;
        }
        for (int i = 0; i < fft_mag_data->size() / 2; ++i)
        {
            data[i + fft_mag_data->size() / 2] = -(static_cast<float>(fft_mag_data->size()) / 2 - (i+1)) * res;
        }
    }


    while(1)
    {
        std::unique_lock<std::mutex> lock(mtx);
        cv.wait(lock, [this](){return data_ready;});
        data_ready = false;
        lock.unlock();

        //// 使用 std::max_element 查找最大值
        //auto maxElement = std::max_element(fft_mag_data->begin()+5, fft_mag_data->begin() + 1948);
        //// 获得索引
        //maxIndex = std::distance(fft_mag_data->begin(), maxElement)+1;

        //// 使用 std::max_element 查找最大值
        //auto maxElement_1 = std::max_element(fft_mag_data->begin() + 2148, fft_mag_data->end() - 5);
        //// 获得索引
        //maxIndex_1 = std::distance(fft_mag_data->begin(), maxElement_1) + 1;

        //if (*maxElement <= *maxElement_1)
        //{
        //    maxIndex = maxIndex_1;
        //}

        //uint16_t pr = (*fft_mag_data)[maxIndex - 2];
        //uint16_t c = (*fft_mag_data)[maxIndex - 1];
        //uint16_t nt = (*fft_mag_data)[maxIndex];

        //maxIndex = (c > pr && c > nt) ? (maxIndex = maxIndex) : (maxIndex = 0);
        int average,sum = 0;
        for (int i = 0; i < fft_mag_data->size(); ++i)
        {
            sum += (*fft_mag_data)[i];
        }
        average = sum / fft_mag_data->size();


        result = findpeaks(*fft_mag_data, 50, average*4);

        maxIndex = 0;

        if (mode == MODE_LEVEL)
        {
            for (int i = 0; i < result.begin()->first.size(); i++)
            {
                uint16_t index = result.begin()->first[i];
                index++;
                if (index >= 5 && index <= 2047)
                {
                    maxIndex = index;
                    break;
                }
            }
        }
        else if (mode == MODE_LIQUID)
        { 
            for (int i = 0; i < result.begin()->first.size(); i++)
            {
                uint16_t index = result.begin()->first[i];
                index++;
                if ((index >= 5 && index <= 1948) || (index >= 2148 && index <= fft_mag_data->size() - 5))
                {
                    maxIndex = index;
                    break;
                }
            }
        }

        if (mode == MODE_LEVEL)
        {
            //float range_t = (maxIndex * res1);

            //range = range_t;
            if (range_update_flag)
            {
                range_update_flag = false;
            }
            else
            {
                range = 0.0f;
            }

            qDebug() << "range is:" << range ;

            if (ui->data_check->isChecked())
            {
                obj_data_ready = true;
                obj_cv.notify_all();
            }
        }
        // 获得速度
        else if (mode == MODE_LIQUID)
        {
            float speed_t = (maxIndex <= (fft_mag_data->size()>>1)) ? (maxIndex * res) : ((maxIndex - static_cast<int>(fft_mag_data->size())) * res);

            speed = speed_t;
            if (ui->data_check->isChecked())
            {
                obj_data_ready = true;
                obj_cv.notify_all();
            }
        }

        if(point_data.size() < 5)
        {
            QList<QPointF> temp, temp1;
            if (mode == MODE_LIQUID)
            {
                for (int i = 0; i < (fft_mag_data->size()>>1); ++i)
                {
                    qreal x = data[i + (fft_mag_data->size()>>1)];
                    qreal y = 20 * log10f(static_cast<float>((*fft_mag_data)[i + (fft_mag_data->size() >> 1)]));
                    temp << QPointF(x, y);
                }
                for (int i = 0; i < (fft_mag_data->size()>>1); ++i)
                {
                    qreal x = data[i];
                    qreal y = 20 * log10f(static_cast<float>((*fft_mag_data)[i]));
                    temp << QPointF(x, y);
                }
                qreal t = 20 * log10f(static_cast<float>(average * 2));
                for (int i = 0; i < fft_mag_data->size(); ++i)
                {
                    qreal x = data[i];
                    qreal y = t;
                    temp1 << QPointF(x, y);
                }

            }
            else
            {
                for (int i = 0; i < fft_mag_data->size(); ++i)
                {
                    qreal x = data[i];
                    qreal y = 20 * log10f(static_cast<float>((*fft_mag_data)[i]));
                    temp << QPointF(x, y);
                }
                qreal t = 20 * log10f(static_cast<float>(average * 4));
                for (int i = 0; i < fft_mag_data->size(); ++i)
                {
                    qreal x = data[i];
                    qreal y = t;
                    temp1 << QPointF(x, y);
                }
            }
            lock.lock();
            point_data.push(temp);
            threshold_data.push(temp1);
            lock.unlock();
        }

        //std::this_thread::sleep_for(std::chrono::milliseconds(10));

    }
}

void MainWindow::data_display(QByteArray* ptr, uint16_t* devaddr)
{
    //qDebug() << "size is:" << ptr->size();

    int judge_mode = 0;
    if (*devaddr == 0x5207)
    {
        judge_mode = MODE_LIQUID;
    }
    else if (*devaddr == 0x5206)
    {
        judge_mode = MODE_LEVEL;
    }

	std::vector<uint16_t>* fft_data_ptr = fft_mag_data.get();

    fft_mag_data->resize(ptr->size() / 2);
    /*自动切换*/
    if (judge_mode != mode)
    {
        if (judge_mode == MODE_LIQUID)
        {
            mode = MODE_LIQUID;
            ui->liquid_radio->setChecked(true);
        }
        else if (judge_mode == MODE_LEVEL)
        {
            mode = MODE_LEVEL;
            ui->level_radio->setChecked(true);
        }
        mode_handle();
    }

	for (int i = 0; i < ptr->size(); i += 2)
	{
        uint8_t reg_h = ptr->at(i + 1);
        uint8_t reg_l = ptr->at(i);
		(*fft_data_ptr)[i >> 1] = ((reg_h << 8) | reg_l);
	}

	data_ready = true;
	cv.notify_all();

    if (ui->obj_check->isChecked())
    {
        file_data_ready = true;
        file_cv.notify_all();
    }
}

void MainWindow::range_display(QByteArray* ptr)
{
    //qDebug() << "range size is:" << ptr->size();

    range_temp_num = ptr->size() / 4;

    memcpy(&range, ptr->data(), sizeof(float));

    std::vector<float> temp;

    memcpy(range_temp, ptr->data(), range_temp_num * sizeof(float));

    for (int i = 0; i < range_temp_num; i++)
    {
        if (range_temp[i] > 0.5)
        {
            temp.push_back(range_temp[i]);
        }
    }

    memset(range_temp, 0, sizeof(range_temp));

    range_temp_num = temp.size();

    for (int i = 0; i < temp.size(); i++)
    {
        range_temp[i] = temp[i];
    }
    memcpy(&range, range_temp, sizeof(float));

#if 1
    tracker1D_tracking(range_temp, range_temp_num, 0.6);

    tracker1D_output = tracker1D_get_output();
#endif

    //qDebug() << "range is: ";
    //for (uint32_t i = 0; i < range_temp_num; i++)
    //{
    //    qDebug() << range_temp[i]<<"m \t";
    //}

    //qDebug() << '\n';

    range_update_flag = true;
}

std::map<std::vector<uint16_t>, std::vector<int>>
MainWindow::findpeaks(const std::vector<uint16_t>& vec, const uint16_t peak_num, const uint16_t threshold)
{
    std::vector<uint16_t> mask(vec.size(), 0);
    std::vector<uint16_t> peaks;
    std::vector<int> dBs;

    std::map<std::vector<uint16_t>, std::vector<int>> map;

    uint8_t mode = 0;

    if (threshold)
    {
        mode = 1;
    }


    for (int i = 0; i < vec.size() - 2; ++i)
    {
        uint16_t k1 = vec[i];
        uint16_t k2 = vec[i + 1];
        uint16_t k3 = vec[i + 2];
        if ((k2 >= k1 && k2 > k3) || (k2 > k1 && k2 >= k3))
        {
            if (mode)
            {
                if (k2 >= threshold)
                {
                    mask[i + 1] |= 0x01;
                }
                else
                {
                    ++i;
                }
            }
            else
            {
                mask[i + 1] |= 0x01;
            }
        }
        else if ((k1 > k2) && (k2 >= k3))
        {
            ++i;
        }
        else 
        {

        }
    }
    //peakcount
    for (uint16_t pkc = 0; pkc < peak_num; ++pkc)
    {
        int maxPeakIndex = -1;
        int maxPeakValue = -1;
        for (int i = 0; i < vec.size(); i++)
        {
            if (mask[i])
            {
                if (vec[i] > maxPeakValue)
                {
                    maxPeakValue = vec[i];
                    maxPeakIndex = i;
                }
            }

        }
        if (maxPeakIndex != -1 && maxPeakValue != -1)
        {
            mask[maxPeakIndex] = 0x00;
            peaks.push_back(maxPeakIndex);
            dBs.push_back(maxPeakValue);
        }
    }
    map.insert(std::make_pair(peaks, dBs));
    return map;

}



void MainWindow::data_store_task()
{
    QString folderPath = QApplication::applicationDirPath() + QDir::separator() + "log" + QDir::separator() + current_time;
    std::vector<uint16_t>* fft_data_ptr = fft_mag_data.get();
    int speed_count = 0;
    int range_count = 0;
    while (1)
    {

        std::unique_lock<std::mutex> lock(file_mtx);
        file_cv.wait(lock, [this]() {return file_data_ready; });
        file_data_ready = false;

        QString filename;
        if(ui->liquid_radio->isChecked())
        {
            filename = QString("%1/speed/%2.bin").arg(folderPath).arg(speed_count);
            speed_count += 1;
        }
        else if(ui->level_radio->isChecked())
        {
            filename = QString("%1/range/%2.bin").arg(folderPath).arg(range_count);
            range_count += 1;
        }

        QFile* file = new QFile(filename);
        if (file->open(QFile::WriteOnly | QIODevice::Truncate))
        {

            QDataStream stream(file);
            
            for (uint16_t i = 0; i < fft_data_ptr->size(); i++)
            {
                stream << (*fft_data_ptr)[i];
            }
            file->close();
        }

    }
}



void MainWindow::obj_store_task()
{
    QString folderPath = QApplication::applicationDirPath() + QDir::separator() + "log" + QDir::separator() + current_time;
    while (1)
    {
        std::unique_lock<std::mutex> lock(obj_mtx);
        obj_cv.wait(lock, [this]() {return obj_data_ready; });
        obj_data_ready = false;
        
        QString filename;
        if (ui->liquid_radio->isChecked())
        {
            filename = QString("%1/speed/speed_data.dat").arg(folderPath);
        }
        else if(ui->level_radio->isChecked())
        {
            filename = QString("%1/range/obj_data.dat").arg(folderPath);
        }

        QFile* file = new QFile(filename);
        if (file->open(QFile::WriteOnly | QIODevice::Append | QFile::Text))
        {
            QTextStream  stream(file);

            stream.setCodec("UTF-8");

            QString str = QDateTime::currentDateTime().toString("MM/dd hh:mm:ss");

            stream << str <<"               ";
            
            if (ui->level_radio->isChecked())
            {
                stream << str << "-czt  ";

                for (uint32_t i = 0; i < range_temp_num; i++)
                {
                    stream << QString::asprintf("%.5f   ", range_temp[i]);
                }
#if 1
                stream << str << "-kalman  ";
                for (uint32_t i = 0; i < tracker1D_output->m_cnt; i++)
                {
                    stream << QString("r%1:").arg(tracker1D_output->m_data[i].m_id) + QString::asprintf("%.5f   ", tracker1D_output->m_data[i].m_distance);
                }
#endif
                stream << "\n";

            }
            else if (ui->liquid_radio->isChecked())
            {
                stream << QString::asprintf("%.5f   ", speed);
                stream << "\n";
            }

            file->close();
        }
    }
}